package spring.framework.context.event;

import spring.framework.beans.BeansException;
import spring.framework.beans.factory.BeanFactory;
import spring.framework.beans.factory.BeanFactoryAware;
import spring.framework.context.ApplicationEvent;
import spring.framework.context.ApplicationListener;
import spring.framework.util.ClassUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

/**
 * @author pengzh
 * @desc
 * @class AbstractApplicationEventMulticaster
 * @since 2021-10-25
 */
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {

    private final Set<ApplicationListener> listenerSet = new LinkedHashSet();

    private BeanFactory beanFactory;

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        listenerSet.add(listener);
    }

    @Override
    public void removeApplicaitonListener(ApplicationListener<?> listener) {
        listenerSet.remove(listener);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    protected Collection<ApplicationListener> getApplicationListener(ApplicationEvent event) {
        LinkedList<ApplicationListener> allListener = new LinkedList();
        for (ApplicationListener listener : listenerSet) {
            if (supportEvent(event, listener)) {
                allListener.add(listener);
            }
        }
        return allListener;
    }


    /**
     * 是否支持该事件的监听
     */
    private boolean supportEvent(ApplicationEvent event, ApplicationListener listener) {

        Class<? extends ApplicationListener> clazz = listener.getClass();
        Class<?> listenerClass = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;
        Type type = listenerClass.getAnnotatedInterfaces()[0].getType();
        Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];
        String className = actualTypeArgument.getTypeName();
        Class<?> eventClassName;
        try {
            eventClassName = Class.forName(className);
        } catch (Exception e) {
            throw new BeansException("wrong class name:" + className);
        }
        //判定该listen监听的事件是不是入参的子关系
        return eventClassName.isAssignableFrom(event.getClass());
    }

}
